package lex 

import "bufio"
import "unicode"

type Lexer struct {
  r *bufio.Reader
  ch rune
  pending bool
  ltype Type
  spelling string
  }
  
const RUNEOF = -1

type Type int

const (
  WRONG Type = iota
  ID
  NUM
  DOT
  LPAR
  RPAR
  BAR
  ARROW
  NL
  EOF
  )

func (t Type) String() string {
  switch t {
    case WRONG: return "WRONG"
    case ID: return "ID"
    case NUM: return "NUM"
    case DOT: return "DOT"
    case LPAR: return "LPAR"
    case RPAR: return "RPAR"
    case BAR: return "BAR"
    case NL: return "newline"
    case EOF: return "EOF"
    }
    return "Type(" + string(t) + ")"
  }

func (l *Lexer) remember(ch rune) {
  l.ch = ch 
  }

func (l *Lexer) rdch() rune {
  ch := l.ch
  if ch == 0 { 
    xch, _, err := l.r.ReadRune() 
    ch = xch
    if err != nil { ch = RUNEOF }
    } else { l.ch = 0 }
  return ch
  }

func (l *Lexer) PutBack( t Type, s string ) {
  l.pending, l.ltype, l.spelling = true, t, s
  }

func (l *Lexer) Next() (t Type, s string) {
  if l.pending {
    l.pending = false
    return l.ltype, l.spelling
    }
  rune := l.rdch()
  for rune == ' ' { rune = l.rdch() }
  switch 
    {
    case unicode.IsLetter(rune):
      for unicode.IsLetter(rune) {
        s += string(rune)
        rune = l.rdch()
        }
      t = ID
      l.remember( rune )

    case unicode.IsDigit(rune):
      for unicode.IsDigit(rune) {
        s += string(rune)
        rune = l.rdch()
        }
       t = NUM
       l.remember( rune )

    case rune == '\n':
      s = string(rune)
      t = NL

    case rune == '(':
      s = string(rune)
      t = LPAR

    case rune == ')':
      s = string(rune)
      t = RPAR

    case rune == '.':
      s = string(rune)
      t = DOT

    case rune == '|':
      s = string(rune)
      t = BAR

    case rune == RUNEOF:
      s = ""
      t = EOF

    case rune == '=':
      rune = l.rdch()
      if rune == '>' { s = "=>"; t = ARROW } else { s = "=?"; t = WRONG }
      
    default:
      s = string(rune)
      t = WRONG

    } 
  return
  }

func Lex( r *bufio.Reader ) *Lexer {
  return &Lexer{r, 0, false, WRONG, ""}
  }

